// 知识点：axios请求封装
import axios, { type Method } from 'axios'
import { useUserStore } from '../stores'
import { showToast } from 'vant'
import router from '@/router'

export const baseURL = 'https://consult-api.itheima.net/'
// const baseURL = 'http://localhost:5173/' // vite.config.ts中配置了代理

const http = axios.create({
  // 1.设置基础地址，超时时间
  baseURL,
  timeout: 10000,
})

http.interceptors.request.use(
  (config) => {
    // 上传文件需要代理服务器转发到本地的express服务器
    if (config.url?.startsWith('/upload')) {
      config.baseURL = '/'
    }

    const userStore = useUserStore()
    // 2.请求携带token
    // 知识点：不管是TS项目还是其他项目，这里都需要判断 userStore.user 是否有值
    if (userStore.user && userStore.user.token) {
      config.headers.Authorization = `Bearer ${userStore.user.token} `
    }

    return config
  },
  (err) => Promise.reject(err),
)

http.interceptors.response.use(
  (res) => {
    // 3.处理业务失败场景
    //知识点： 处理业务失败
    // 返回失败的 Promise 对象，把错误的返回信息data传递出去，让catch的时候可以接收到（自己也可以根据catch到的data里面的code进行不同的操作）
    if (res.data.code !== 10000) {
      showToast(res.data.message || '请求失败')

      // 知识点：返回new error 是标准的js写法，但是会导致catch到的err是一个error对象，error对象的message才是后端返回的错误信息
      // 这样会导致下文catch到的err信息不直观，所以这里直接返回后端返回的错误信息，而不是包装到error里面（实际业务失败的场景都这样做）
      // return Promise.reject(new Error(res.data || '请求 失败'))
      return Promise.reject(res.data || '请求 失败')
    }

    // 4. 摘取核心响应数据
    return res.data
  },
  (err) => {
    // 5. 处理401错误
    if (err.response?.status === 401) {
      // 删除本地和pinia的用户信息（注意：需要配置了持久化）
      useUserStore().clearUser()
      router.push({
        path: '/login',
        query: { redirect: router.currentRoute.value.fullPath },
      })
    }
    return Promise.reject(err)
  },
)

export default http

// 6.【知识点：】封装发送请求的工具函数，简化调用(实际调用接口使用这个函数)
// 下面的Method是axios中定义的类型，用来描述请求方法

// 定义接口返回值的类型，一般接口返回值结构都是固定的
type ResData<T> = {
  code: number
  message: string
  data: T
}
// <T>泛型T定义的是data属性的类型，因为返回值中只有它的动态的
export const request = <T>(url: string, method: Method = 'GET', submitData?: object) => {
  // 方法说明：
  // 参数: 地址, 请求方式, 提交的数据
  // 返回: promise

  // 知识点：为什么request的泛型参数设置的是第二个？原因如下（可以点开http.request的源码看看它的类型定义）：
  // 泛型可能定义了多个值，它们是有顺序的
  // 1.第一个参数 → 约束响应数据中 data属性值 的类型（核心关注的业务数据）
  // 2.第二个参数 → 约束整个响应对象的类型（通常是框架内部的响应结构，如包含 code、status 等字段）

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  return http.request<any, ResData<T>>({
    url,
    method,
    [method.toUpperCase() === 'GET' ? 'params' : 'data']: submitData,
  })
}
